/*
 * sgml.l
 * This file is part of gURL
 *
 * Copyright (C) 2014 - Aleksey Konovalov
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Library General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* $Id: sgml.l,v 1.9 1996/02/07 15:32:28 connolly Exp $ */
/* sgml.l -- a lexical analyzer for Basic+/- SGML Documents
 * See: "A Lexical Analyzer for HTML and Baisc SGML"
 */

/*
 * NOTE: We assume the locale used by lex and the C compiler
 * agrees with ISO-646-IRV; for example: '1' == 0x31.
 */

%{

#include <gtk/gtk.h>
#include "lex_common.h"

#define TAG_ATTRS			"sgml_tag", "foreground", "#200080", "weight", PANGO_WEIGHT_BOLD, NULL

#define NUMERIC_CHARACTER_ATTRS		"sgml_numeric_character", NULL
#define ERROR_ATTRS			"sgml_error", "background", "#ff0000", NULL
#define NAMED_CHARACTER_ATTRS		"sgml_named_character", NULL
#define GEREF_ATTRS			"sgml_geref", NULL
#define END_TAG_ATTRS			TAG_ATTRS
#define EMPTY_TAG_ATTRS 		TAG_ATTRS
#define MARKUP_DECL_ATTRS		NAME_ATTRS
#define EMPTY_COMMENT_ATTRS		COMMENT_ATTRS
#define COMMENT_ATTRS			"sgml_comment", "foreground", "#595979", NULL
#define PROCESSING_INSTRUCTION_ATTRS	"sgml_processing_instruction", "foreground", "#008c00", NULL
#define START_TAG_ATTRS			TAG_ATTRS
#define DATA_ATTRS			"sgml_data", NULL
#define NET_ATTRS			"sgml_net", NULL
#define ATTRNAME_ATTRS			"sgml_attrname", "foreground", "#474796",NULL
#define LITERAL_ATTRS			"sgml_literal", "foreground", "#008c00", NULL
#define NMTOKEN_ATTRS			LITERAL_ATTRS
#define TAG_CLOSE_ATTRS			TAG_ATTRS
#define PERO_ATTRS			"sgml_pero", NULL
#define NUMBER_ATTRS			"sgml_numer", NULL
#define NAME_ATTRS			"sgml_name", "foreground", "#004a43", NULL
#define NUMTOKEN_ATTRS			"sgml_numtoken", NULL
#define DSO_ATTRS			"sgml_dso", NULL

%}

%option prefix="sgml"
%option outfile="lex.yy.c"
%option noyywrap
%option reentrant
%option extra-type="struct ParseData*"
%option yylineno

/* Figure 1 -- Character Classes: Abstract Syntax */

Digit		[0-9]
LCLetter	[a-z]
Special		['()_,\-\./:=?]
UCLetter	[A-Z]

/* Figure 2 -- Character Classes: Concrete Syntax */

LCNMCHAR	[\.-]
/* LCNMSTRT	[] */
UCNMCHAR	[\.-]
/* UCNMSTRT	[] */
 /* @# hmmm. sgml spec says \015 */
RE		\n
 /* @# hmmm. sgml spec says \012 */
RS		\r
SEPCHAR		\011
SPACE		\040

/* Figure 3 -- Reference Delimiter Set: General */

COM	"--"
CRO	"&#"
DSC	"]"
DSO	"["
ERO	"&"
ETAGO	"</"
LIT	\"
LITA	"'"
MDC	">"
MDO	"<!"
MSC	"]]"
NET     "/"
PERO    "%"
PIC	">"
PIO	"<?"
REFC	";"
STAGO	"<"
TAGC	">"

/* 9.2.1 SGML Character */

/*name_start_character	{LCLetter}|{UCLetter}|{LCNMSTRT}|{UCNMSTRT}*/
name_start_character	{LCLetter}|{UCLetter}
name_character		{name_start_character}|{Digit}|{LCNMCHAR}|{UCNMCHAR}

/* 9.3 Name */

name		{name_start_character}{name_character}*
number		{Digit}+
number_token	{Digit}{name_character}*
name_token	{name_character}+

/* 6.2.1 Space */
s		{SPACE}|{RE}|{RS}|{SEPCHAR}
ps		({SPACE}|{RE}|{RS}|{SEPCHAR})+

/* trailing white space */
ws		({SPACE}|{RE}|{RS}|{SEPCHAR})*

/* 9.4.5 Reference End */
reference_end	({REFC}|{RE})

/*
 * 10.1.2 Parameter Literal
 * 7.9.3  Attribute Value Literal
 * (we leave recognition of character references and entity references,
 *  and whitespace compression to further processing)
 *
 * @# should split this into minimum literal, parameter literal,
 * @# and attribute value literal.
 */
literal		({LIT}[^\"]*{LIT})|({LITA}[^\']*{LITA})



/* 9.6.1 Recognition modes */

/*
 * Recognition modes are represented here by start conditions.
 * The default start condition, INITIAL, represents the
 * CON recognition mode. This condition is used to detect markup
 * while parsing normal data charcters (mixed content).
 *
 * The CDATA start condition represents the CON recognition
 * mode with the restriction that only end-tags are recognized,
 * as in elements with CDATA declared content.
 * (@# no way to activate it yet: need hook to parser.)
 *
 * The TAG recognition mode is split into two start conditions:
 * ATTR, for recognizing attribute value list sub-tokens in
 * start-tags, and TAG for recognizing the TAGC (">") delimiter
 * in end-tags.
 *
 * The MD start condition is used in markup declarations. The COM
 * start condition is used for comment declarations.
 *
 * The DS condition is an approximation of the declaration subset
 * recognition mode in SGML. As we only use this condition after signalling
 * an error, it is merely a recovery device.
 *
 * The CXT, LIT, PI, and REF recognition modes are not separated out
 * as start conditions, but handled within the rules of other start
 * conditions. The GRP mode is not represented here.
 */

 /* EXCERPT ACTIONS: START */

/* %x CON == INITIAL */
%x CDATA

%x TAG
%x ATTR
%x ATTRVAL
%x NETDATA
%x ENDTAG
/* this is only to be permissive with bad end-tags: */
%x JUNKTAG

%x MD
%x COM
%x DS

 /* EXCERPT ACTIONS: STOP */

%%

\r|\n		yyextra->text = yytext + 1;

 /*
  * 9.6 Delimiter Recognition and
  * Figure 3 -- Reference Delimiter Set: General
  *
  * This is organized by recognition mode: first CON, then TAG,
  * MD, and DS. Within a mode, the rules are ordered alphabetically
  * by delimiter name.
  */


  /* &#60; -- numeric character reference */
<INITIAL,NETDATA>{CRO}{number}{reference_end}?	 	MARK_TEXT(NUMERIC_CHARACTER_ATTRS);

  /* &#60xyz. -- syntax error */
<INITIAL,NETDATA>{CRO}{number_token}{reference_end}?	MARK_TEXT(ERROR_ATTRS);

  /* &#SPACE; -- named character reference. */
<INITIAL,NETDATA>{CRO}{name}{reference_end}?		MARK_TEXT(NAMED_CHARACTER_ATTRS);

  /* &amp; -- general entity reference */
<INITIAL,NETDATA>{ERO}{name}{reference_end}?		MARK_TEXT(GEREF_ATTRS);

  /* </name <  -- unclosed end tag */
<INITIAL,CDATA>{ETAGO}{name}?{ws}/{STAGO}		MARK_TEXT(END_TAG_ATTRS);

  /* </title> -- end tag */
<INITIAL,CDATA>{ETAGO}{name}{ws} {
                                     MARK_TEXT(END_TAG_ATTRS);
			             BEGIN(ENDTAG);
				 }

  /* @# HACK for XMP, LISTING?
  Date: Fri, 19 Jan 1996 23:13:43 -0800
  Message-Id: <v01530502ad25cc1a251b@[206.86.76.80]>
  To: www-html@w3.org
  Subject: Re: Daniel Connolly's SGML Lex Specification
  */

  /* @@ all these are recognized in NETDATA too. Need a stack? */

  /* </> -- empty end tag */
{ETAGO}{TAGC}			MARK_TEXT(EMPTY_TAG_ATTRS);

  /* <!DOCTYPE -- markup declaration */
{MDO}{name}{ws}			{
				    MARK_TEXT(MARKUP_DECL_ATTRS);
				    BEGIN(MD);
				}

  /* <!> -- empty comment */
{MDO}{MDC}			MARK_TEXT(EMPTY_COMMENT_ATTRS);

  /* <!--  -- comment declaration */
{MDO}/{COM}			{
                                    MARK_TEXT(COMMENT_ATTRS);
				    BEGIN(COM);
				}

  /* <![ -- marked section */
{MDO}{DSO}{ws}		{
			    BEGIN(DS); /* @# skip past some stuff */
		      	}

  /* ]]> -- marked section end */
{MSC}{MDC}	{}

  /* <? ...> -- processing instruction */
{PIO}[^>]*{PIC}			MARK_TEXT(PROCESSING_INSTRUCTION_ATTRS);

  /* <name -- start tag */
{STAGO}{name}{ws}		{
                                    MARK_TEXT(START_TAG_ATTRS);
				    BEGIN(ATTR);
				}


  /* <> -- empty start tag */
{STAGO}{TAGC}			MARK_TEXT(START_TAG_ATTRS);

  /* abcd -- data characters */
([^<&]|(<[^<&a-zA-Z!->?])|(&[^<&#a-zA-Z]))+|.	MARK_TEXT(DATA_ATTRS);

  /* abcd -- data characters */
<CDATA>[^<]+|.			MARK_TEXT(DATA_ATTRS);

<NETDATA>{NET}	{
                        MARK_TEXT(NET_ATTRS);
			BEGIN(INITIAL);
		    }

  /* <em/ ^abcd  / -- data characters within null end tag */
<NETDATA>([^/&<])+|. MARK_TEXT(DATA_ATTRS);

 /* 7.4 Start Tag */
 /* Actually, the generic identifier specification is consumed
  * along with the STAGO delimiter ("<"). So we're only looking
  * for tokens that appear in an attribute specification list,
  * plus TAGC (">"). NET ("/") and STAGO ("<") signal limitations.
  */

 /* 7.5 End Tag */
 /* Just looking for TAGC. NET, STAGO as above */

  /* <a ^href = "xxx"> -- attribute name */
<ATTR>{name}{s}*={ws}		{
                                  MARK_TEXT(ATTRNAME_ATTRS);
				  BEGIN(ATTRVAL);
				}

  /* <img src="xxx" ^ismap> -- name */
<ATTR>{name}{ws}		MARK_TEXT(ATTRNAME_ATTRS);

  /* <a name = ^xyz> -- name token */
<ATTRVAL>{name_token}{ws}	{
                                  MARK_TEXT(NMTOKEN_ATTRS);
				  BEGIN(ATTR);
				}

  /* <a href = ^"a b c"> -- literal */
<ATTRVAL>{literal}{ws}		{
                                  MARK_TEXT(LITERAL_ATTRS);
				  BEGIN(ATTR);
				}

  /* <a name= ^> -- illegal tag close */
<ATTRVAL>{TAGC}			{
				  MARK_TEXT(TAG_CLOSE_ATTRS);
				  BEGIN(INITIAL);
				}

  /* <a name=foo ^>,</foo^> -- tag close */
<ATTR,TAG>{TAGC}		{
				  MARK_TEXT(TAG_CLOSE_ATTRS);
				  BEGIN(INITIAL);
				}

  /* <em^/ -- NET tag */
<ATTRVAL>{NET}	{
			 MARK_TEXT(NET_ATTRS);
			 BEGIN(INITIAL);
		       }

  /* <em^/ -- NET tag */
<ATTR>{NET}	{
  			  MARK_TEXT(NET_ATTRS);
			  BEGIN(NETDATA);
 			}

  /* <foo^<bar> -- unclosed start tag */
<ATTR,ATTRVAL,TAG>{STAGO}	{
                         /* report pending tag */
			 BEGIN(INITIAL);
                           /* save STAGO for next time */
#ifdef FIX_YYLESS /*@@*/
                           yyless(leng-1); /*@# length of STAGO assumed 1 */
#endif
                           BEGIN(INITIAL);
		       }

  /* <a href = ^http://foo/> -- unquoted literal HACK */
<ATTRVAL>[^ "\t\n\r>]+{ws}	{
                                  MARK_TEXT(LITERAL_ATTRS);
				  BEGIN(ATTR);
				}

<ATTR,ATTRVAL,TAG>.	{
		       }

  /* end tag -- non-permissive */
<ENDTAG>{TAGC} {
                        MARK_TEXT(TAG_CLOSE_ATTRS);
			BEGIN(INITIAL);
	       }

<ENDTAG>. {
		    }

  /* permissive search for tag close */
<JUNKTAG>[^>]+ {
			/* skip */
		    }

<JUNKTAG>{TAGC} {
			BEGIN(INITIAL);
                }


 /* 10 Markup Declarations: General */

 /* <!^--...-->   -- comment */
<MD,COM>{COM}([^-]|-[^-])*{COM}{ws}	MARK_TEXT(COMMENT_ATTRS);

 /* <!doctype ^%foo;> -- parameter entity reference */
<MD>{PERO}{name}{reference_end}?{ws}	MARK_TEXT(PERO_ATTRS);

 /* <!entity ^% foo system "..." ...> -- parameter entity definition */
<MD>{PERO}{ps}			MARK_TEXT(PERO_ATTRS);
 
 /* The limited set of markup delcarations we're interested in
  * use only numbers, names, and literals.
  */
<MD>{number}{ws}		MARK_TEXT(NUMBER_ATTRS);

<MD>{name}{ws}			MARK_TEXT(NAME_ATTRS);

<MD>{number_token}{ws}		MARK_TEXT(NUMTOKEN_ATTRS);

<MD>{name_token}{ws}		MARK_TEXT(NMTOKEN_ATTRS);

<MD>{literal}{ws}	        MARK_TEXT(LITERAL_ATTRS);

<MD,COM>{MDC}			{
                                  MARK_TEXT(TAG_CLOSE_ATTRS);
				  BEGIN(INITIAL);
			        }

 /* other constructs are errors. */
   /* <!doctype foo ^[  -- declaration subset */
<MD>{DSO}			{
			 MARK_TEXT(DSO_ATTRS);
			 BEGIN(DS);
		       }

<MD,COM>.				{
		       }


 /* 10.4 Marked Section Declaration */
 /* 11.1 Document Type Declaration Subset */

 /* Our parsing of declaration subsets is just an error recovery technique:
  * we attempt to skip them, but we may be fooled by "]"s
  * inside comments, etc.
  */

  /* ]]> -- marked section end */
<DS>{MSC}{MDC}			{
				 BEGIN(INITIAL);
				}
  /* ] -- declaration subset close */
<DS>{DSC}			{ BEGIN(COM); }

<DS>[^\]]+			{
		       }

 /* EXCERPT ACTIONS: STOP */

%%

void highlight_sgml(GtkTextBuffer* tb, const gchar* text, size_t len,
                    gboolean prettyprint) {
	DO_HIGHLIGHT(sgml, tb, text, len, FALSE /*TODO prettyprint*/ );
}
